package com.beardedhen.harmonybootstrap;

import com.beardedhen.harmonybootstrap.api.attributes.BootstrapBrand;
import com.beardedhen.harmonybootstrap.api.attributes.ViewGroupPosition;
import com.beardedhen.harmonybootstrap.api.defaults.DefaultBootstrapBrand;
import com.beardedhen.harmonybootstrap.api.defaults.DefaultBootstrapSize;
import com.beardedhen.harmonybootstrap.api.defaults.ExpandDirection;
import com.beardedhen.harmonybootstrap.api.view.BootstrapBrandView;
import com.beardedhen.harmonybootstrap.api.view.OutlineableView;
import com.beardedhen.harmonybootstrap.api.view.RoundableView;
import com.beardedhen.harmonybootstrap.utils.ColorUtils;
import com.beardedhen.harmonybootstrap.utils.DimenUtils;
import com.beardedhen.harmonybootstrap.utils.LogUtil;
import com.beardedhen.harmonybootstrap.utils.UiUtil;
import com.beardedhen.harmonybootstrap.utils.ViewUtils;

import ohos.agp.components.AttrSet;
import ohos.agp.components.Component;
import ohos.agp.components.ComponentContainer;
import ohos.agp.components.ComponentState;
import ohos.agp.components.DirectionalLayout;
import ohos.agp.components.ScrollView;
import ohos.agp.components.Text;
import ohos.agp.components.element.Element;
import ohos.agp.render.Paint;
import ohos.agp.utils.Color;
import ohos.agp.utils.LayoutAlignment;
import ohos.agp.utils.TextAlignment;
import ohos.agp.window.dialog.BaseDialog.DialogListener;
import ohos.agp.window.dialog.PopupDialog;
import ohos.agp.window.service.DisplayManager;
import ohos.app.Context;
import ohos.global.resource.NotExistException;
import ohos.global.resource.ResourceManager;
import ohos.global.resource.WrongTypeException;

import java.io.IOException;
import java.util.regex.Pattern;

/**
 * BootstrapButtons are buttons which provide contextual menus, styled with BootstrapBrand colors,
 * roundable corners, and an 'outlineable' mode.
 */

public class BootstrapDropDown extends Text
    implements Component.ClickedListener, RoundableView, OutlineableView, BootstrapBrandView, DialogListener {

    private static final String TAG = "com.beardedhen.harmonybootstrap.BootstrapDropDown";

    private int[] colorList;

    private static final String SEARCH_REGEX_HEADER = "\\{dropdown_header\\}.*";

    private static final String SEARCH_REGEX_SEPARATOR = "\\{dropdown_separator\\}.*";

    private static final String SEARCH_REGEX_DISABLED = "\\{dropdown_disabled\\}.*";

    private static final String REPLACE_REGEX_HEADER = "\\{dropdown_header\\}";

    private static final String REPLACE_REGEX_SEPARATOR = "\\{dropdown_separator\\}";

    private static final String REPLACE_REGEX_DISABLED = "\\{dropdown_disabled\\}";

    private ExpandDirection expandDirection;

    private PopupDialog dropdownWindow;

    private ClickedListener clickListener;

    private String[] dropdownData;

    private String[] cleandropdownData;

    private OnDropDownItemClickListener onDropDownItemClickListener;

    private static final String BOOTSTRAP_DROP_DOWN_ROUNDED_CORNERS = "roundedCorners";

    private static final String BOOTSTRAP_DROP_DOWN_SHOW_OUTLINE = "showOutline";

    private static final String BOOTSTRAP_DROP_DOWN_BOOTSTRAP_EXPAND_DIRECTION = "bootstrapExpandDirection";

    private static final String BOOTSTRAP_DROP_DOWN_DROPDOWN_RESOURCE = "dropdownResource";

    private static final String BOOTSTRAP_DROP_DOWN_BOOTSTRAP_SIZE = "bootstrapSize";

    private static final String BOOTSTRAP_DROP_DOWN_BOOTSTRAPBRAND = "bootstrapBrand";

    private static final String BOOTSTRAP_DROP_DOWN_BOOTSTRAP_TEXT = "bootstrapText";

    private boolean roundedCorners;

    private boolean showOutline;

    private float bootstrapSize;

    private int itemHeight;

    private int dropDownViewWidth;

    private int screenWidth;

    private float baselineStrokeWidth;

    private float baselineCornerRadius;

    private float baselineFontSize;

    private float baselineDropDownViewFontSize;

    private float baselineItemLeftPadding;

    private float baselineVertPadding;

    private float baselineHoriPadding;

    private BootstrapBrand bootstrapBrand;

    private String bootstrapText;

    public void onComponentStateChanged(Component component, int state) {
        switch (state) {
            case ComponentState.COMPONENT_STATE_HOVERED:
            case ComponentState.COMPONENT_STATE_CHECKED:
            case ComponentState.COMPONENT_STATE_FOCUSED:
            case ComponentState.COMPONENT_STATE_PRESSED:
            case ComponentState.COMPONENT_STATE_SELECTED:
            case 18432:
            case 2052:
                setTextColor(new Color(colorList[1]));
                break;
            case ComponentState.COMPONENT_STATE_DISABLED:
                setTextColor(new Color(colorList[2]));
                break;
            default:
                setTextColor(new Color(colorList[0]));
                break;
        }

    }

    public BootstrapDropDown(Context context) throws NotExistException, WrongTypeException, IOException {
        super(context);
        initialise(null);

    }

    public BootstrapDropDown(Context context, AttrSet attrs) throws NotExistException, WrongTypeException, IOException {
        super(context, attrs);
        initialise(attrs);

    }

    public BootstrapDropDown(Context context, AttrSet attrs, String defStyle)
        throws NotExistException, WrongTypeException, IOException {
        super(context, attrs, defStyle);
        initialise(attrs);
    }

    private int getDirectionOrdinal(String name) {
        int directionOrdinal = 1;
        if (name.equalsIgnoreCase("UP")) {
            directionOrdinal = 0;
        } else if (name.equalsIgnoreCase("DOWN")) {
            directionOrdinal = 1;
        }
        return directionOrdinal;
    }

    private int getSizeOrdinal(String name) {
        int typeOrdinal = 2;
        if (name.equalsIgnoreCase("XS")) {
            typeOrdinal = 0;
        } else if (name.equalsIgnoreCase("SM")) {
            typeOrdinal = 1;
        } else if (name.equalsIgnoreCase("MD")) {
            typeOrdinal = 2;
        } else if (name.equalsIgnoreCase("LG")) {
            typeOrdinal = 3;
        } else if (name.equalsIgnoreCase("XL")) {
            typeOrdinal = 4;
        }
        return typeOrdinal;
    }

    private static String[] getStringArray(Context context,
        int id) {//Pass openharmony context and Id as ResourceTable.Strarray_month_names
        ResourceManager manager = context.getResourceManager();
        try {
            return manager.getElement(id).getStringArray();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (NotExistException e) {
            e.printStackTrace();
        } catch (WrongTypeException e) {
            e.printStackTrace();
        }
        return null;
    }

    private void initialise(AttrSet attrs) throws NotExistException, WrongTypeException, IOException {
        if (attrs == null) {
            return;
        }
        setComponentStateChangedListener(this::onComponentStateChanged);
        LogUtil.info(TAG, "Entering initialise");

        try {
            roundedCorners = attrs.getAttr(BOOTSTRAP_DROP_DOWN_ROUNDED_CORNERS).isPresent() ? attrs.getAttr(
                BOOTSTRAP_DROP_DOWN_ROUNDED_CORNERS).get().getBoolValue() : false;

            showOutline = attrs.getAttr(BOOTSTRAP_DROP_DOWN_SHOW_OUTLINE).isPresent() ? attrs.getAttr(
                BOOTSTRAP_DROP_DOWN_SHOW_OUTLINE).get().getBoolValue() : false;

            int directionOrdinal = attrs.getAttr(BOOTSTRAP_DROP_DOWN_BOOTSTRAP_EXPAND_DIRECTION).isPresent()
                ? getDirectionOrdinal(attrs.getAttr(BOOTSTRAP_DROP_DOWN_BOOTSTRAP_EXPAND_DIRECTION).get().getStringValue())
                : -1;

            int dataOrdinal = attrs.getAttr(BOOTSTRAP_DROP_DOWN_DROPDOWN_RESOURCE).isPresent() ? attrs.getAttr(
                BOOTSTRAP_DROP_DOWN_DROPDOWN_RESOURCE).get().getIntegerValue() : -1;

            int sizeOrdinal = -1;
            if (attrs.getAttr(BOOTSTRAP_DROP_DOWN_BOOTSTRAP_SIZE).isPresent()) {
                String sizeOrdinalStr = attrs.getAttr(BOOTSTRAP_DROP_DOWN_BOOTSTRAP_SIZE).get().getStringValue().trim();
                sizeOrdinal = UiUtil.getBootstrapSizeInt(sizeOrdinalStr);
            }
            expandDirection = ExpandDirection.fromAttributeValue(directionOrdinal);

            bootstrapSize = DefaultBootstrapSize.fromAttributeValue(sizeOrdinal).scaleFactor();

            itemHeight = (int) DimenUtils.pixelsFromDpResource(getContext(),
                ResourceTable.Float_bootstrap_dropdown_default_item_height);

            int typeOrdinal = -1;
            if (attrs.getAttr(BOOTSTRAP_DROP_DOWN_BOOTSTRAPBRAND).isPresent()) {
                String typeOrdinalStr = attrs.getAttr(BOOTSTRAP_DROP_DOWN_BOOTSTRAPBRAND).get().getStringValue().trim();
                typeOrdinal = UiUtil.getBootstrapBrandInt(typeOrdinalStr);
            }
            bootstrapBrand = DefaultBootstrapBrand.fromAttributeValue(typeOrdinal);

            bootstrapText = attrs.getAttr(BOOTSTRAP_DROP_DOWN_BOOTSTRAP_TEXT).isPresent() ? attrs.getAttr(
                BOOTSTRAP_DROP_DOWN_BOOTSTRAP_TEXT).get().getStringValue() : "Primary";

            if (dataOrdinal > 0) {
                dropdownData = getStringArray(getContext(), dataOrdinal);
            } else {
                dropdownData = new String[] {""};
            }

        } catch (Exception e) {
            e.printStackTrace();
        }

        baselineStrokeWidth = DimenUtils.pixelsFromDpResource(getContext(),
            ResourceTable.Float_bootstrap_dropdown_default_edge_width);

        baselineCornerRadius = DimenUtils.pixelsFromDpResource(getContext(),
            ResourceTable.Float_bootstrap_dropdown_default_corner_radius);

        baselineFontSize = DimenUtils.pixelsFromSpResource(getContext(),
            ResourceTable.Float_bootstrap_dropdown_default_font_size);

        baselineDropDownViewFontSize = DimenUtils.pixelsFromSpResource(getContext(),
            ResourceTable.Float_bootstrap_dropdown_default_item_font_size);

        baselineItemLeftPadding = DimenUtils.pixelsFromDpResource(getContext(),
            ResourceTable.Float_bootstrap_dropdown_default_item_left_padding);

        baselineVertPadding = DimenUtils.pixelsFromDpResource(getContext(),
            ResourceTable.Float_bootstrap_button_default_vert_padding);

        baselineHoriPadding = DimenUtils.pixelsFromDpResource(getContext(),
            ResourceTable.Float_bootstrap_button_default_hori_padding);

        screenWidth = DisplayManager.getInstance().getDefaultDisplay(getContext()).get().getAttributes().width;

        createDropDown();
        updateDropDownState();
    }

    private ScrollView createDropDownView() {
        final DirectionalLayout dropdownView = new DirectionalLayout(getContext());
        ScrollView scrollView = new ScrollView(getContext());
        int clickableChildCounter = 0;

        dropdownView.setOrientation(DirectionalLayout.VERTICAL);
        int height = (int) (itemHeight * bootstrapSize);
        scrollView.setHeight(itemHeight * 7);
        ComponentContainer.LayoutConfig childParams = new ComponentContainer.LayoutConfig(
            ComponentContainer.LayoutConfig.MATCH_PARENT, height);

        for (String text : dropdownData) {
            Text childView = new Text(getContext());
            childView.setTextAlignment(TextAlignment.VERTICAL_CENTER);
            childView.setLayoutConfig(childParams);

            int padding = (int) (baselineItemLeftPadding * bootstrapSize);
            childView.setPadding(padding, 0, padding, 0);
            childView.setTextSize((int) (baselineDropDownViewFontSize * bootstrapSize));
            childView.setTextColor(Color.BLACK);

            childView.setClickedListener(new ClickedListener() {

                @Override
                public void onClick(Component v) {
                    dropdownWindow.hide();
                    if (onDropDownItemClickListener != null) {
                        onDropDownItemClickListener.onItemClick(dropdownView, v, v.getId());
                    }
                }
            });

            if (Pattern.matches(SEARCH_REGEX_HEADER, text)) {
                childView.setText(text.replaceFirst(REPLACE_REGEX_HEADER, ""));
                childView.setTextSize((int) ((baselineDropDownViewFontSize - 2F) * bootstrapSize));
                childView.setClickable(false);
                childView.setTextColor(
                    new Color(ColorUtils.resolveColor(ResourceTable.Color_bootstrap_gray_light, getContext())));
            } else if (Pattern.matches(SEARCH_REGEX_SEPARATOR, text)) {
                childView = new DividerView(getContext());
                childView.setClickable(false);
                childView.setLayoutConfig(
                    new DirectionalLayout.LayoutConfig(ComponentContainer.LayoutConfig.MATCH_PARENT, 3));
            } else if (Pattern.matches(SEARCH_REGEX_DISABLED, text)) {
                childView.setEnabled(false);
                childView.setTextColor(
                    new Color(ColorUtils.resolveColor(ResourceTable.Color_bootstrap_gray_light, getContext())));
                childView.setId(clickableChildCounter++);
                childView.setText(text.replaceFirst(REPLACE_REGEX_DISABLED, ""));
            } else {
                childView.setText(text);
                childView.setId(clickableChildCounter++);
            }
            dropdownView.addComponent(childView);
        }

        dropDownViewWidth = dropdownView.getWidth();

        scrollView.setScrollbarOverlapEnabled(false);
        scrollView.addComponent(dropdownView);

        cleanData();
        return scrollView;
    }

    private void createDropDown() {
        ScrollView dropdownView = createDropDownView();

        dropdownWindow = new PopupDialog(getContext(), dropdownView, ComponentContainer.LayoutConfig.MATCH_CONTENT,
            ComponentContainer.LayoutConfig.MATCH_CONTENT); //width is necessary field to give
        dropdownWindow.setDialogListener(this);

        dropdownWindow.setSize(ComponentContainer.LayoutConfig.MATCH_CONTENT,
            ComponentContainer.LayoutConfig.MATCH_CONTENT);

        dropdownWindow.setCustomComponent(dropdownView);
    }

    private void updateDropDownState() {
        if (bootstrapText != null) {
            setText(String.valueOf(bootstrapText));
        }
        bootstrapBrand = getBootstrapBrand();

        float cornerRadius = baselineCornerRadius;
        float strokeWidth = baselineStrokeWidth;
        final float fontSize = baselineFontSize * bootstrapSize;

        strokeWidth *= bootstrapSize;

        super.setClickedListener(this);
        setTextSize((int) fontSize);
        setAroundElements(null, null, BootstrapDrawableFactory.bootstrapDropDownArrow(getContext(),
            DimenUtils.dpToPixels(getContext(), 8 * bootstrapSize),
            DimenUtils.dpToPixels(getContext(), 12 * bootstrapSize), expandDirection, showOutline, getBootstrapBrand()),
            null);

        setAroundElementsPadding(DimenUtils.dpToPixels(getContext(), 8));

        colorList = BootstrapDrawableFactory.bootstrapButtonText(  //showing error settext cannot return int
            getContext(), showOutline, bootstrapBrand);

        setTextColor(new Color(colorList[0]));

        Element bg = BootstrapDrawableFactory.bootstrapButton(getContext(), bootstrapBrand, (int) strokeWidth,
            (int) cornerRadius, ViewGroupPosition.SOLO, showOutline, roundedCorners);

        ViewUtils.setBackgroundDrawable(this, bg);

        int vert = (int) (baselineVertPadding * bootstrapSize);
        int hori = (int) (baselineHoriPadding * bootstrapSize);
        setPadding(hori, vert, hori, vert);
    }

    private void cleanData() {
        String[] cleanArray = new String[dropdownData.length];
        for (int i = 0; i < dropdownData.length; i++) {
            cleanArray[i] = dropdownData[i].replaceAll(REPLACE_REGEX_HEADER, "")
                .replaceAll(REPLACE_REGEX_DISABLED, "")
                .replaceAll(REPLACE_REGEX_SEPARATOR, "");
        }
        cleandropdownData = cleanArray;
    }

    @Override
    public boolean isShowOutline() {
        return showOutline;
    }

    @Override
    public boolean isRounded() {
        return roundedCorners;
    }

    /**
     * Gets the direction in which the dropdown expands.
     *
     * @return the direction
     */
    public ExpandDirection getExpandDirection() {
        return expandDirection;
    }

    /**
     * Retrieves the data used to populate the dropdown.
     *
     * @return a string array of values
     */
    public String[] getDropdownData() {
        return dropdownData;
    }

    @Override
    public void setShowOutline(boolean showOutline) {
        this.showOutline = showOutline;
        updateDropDownState();
    }

    @Override
    public void setRounded(boolean rounded) {
        roundedCorners = rounded;
        updateDropDownState();
    }

    /**
     * Sets the direction in which the dropdown should expand.
     *
     * @param expandDirection the direction
     */
    public void setExpandDirection(ExpandDirection expandDirection) {
        this.expandDirection = expandDirection;
        updateDropDownState();
    }

    /**
     * Sets the String values which should be used to populate the menu displayed in the dropdown.
     *
     * @param dropdownData an array of string values.
     */
    public void setDropdownData(String[] dropdownData) {
        this.dropdownData = dropdownData;
        createDropDown();
        updateDropDownState();
    }

    private void resetDropDown() {
        if (dropdownWindow != null) {
            dropdownWindow.hide();
            dropdownWindow = null;
        }
        createDropDown();
    }

    @Override
    public void onClick(Component v) {
        if (clickListener != null) {
            clickListener.onClick(v);
        }
        //using 8dip on axisX offset to make dropdown view visually be at start of dropdown itself
        //using 4dip on axisY offset to make space between dropdown view and dropdown itself
        //all offsets are necessary because of the dialog_holo_light_frame to display correctly on screen(shadow was made by inset)
        int coord[] = getLocationOnScreen();
        resetDropDown();
        int gravity;
        int axisXOffset;
        if (dropDownViewWidth + coord[0] > screenWidth) {
            gravity = LayoutAlignment.END;//Gravity.TOP | Gravity.END;
            axisXOffset = DimenUtils.dpToPixels(getContext(), 8);
        } else {
            gravity = LayoutAlignment.START; // Gravity.TOP | Gravity.START;
            axisXOffset = -DimenUtils.dpToPixels(getContext(), 8);
        }
        int axisYOffset = DimenUtils.dpToPixels(getContext(), 4);

        switch (expandDirection) {
            case UP:
                gravity = gravity | LayoutAlignment.BOTTOM;
                dropdownWindow.showOnCertainPosition(gravity, coord[0] + axisXOffset, getHeight() + axisYOffset * 18);

                break;
            case DOWN:
                gravity = gravity | LayoutAlignment.TOP;
                dropdownWindow.showOnCertainPosition(gravity, coord[0] + axisXOffset,
                    coord[1] + getHeight() + axisYOffset * 5);

                break;
        }

        setSelected(true);
    }

    @Override
    public void setClickedListener(ClickedListener clickListener) {
        this.clickListener = clickListener;
    }

    @Override
    public void setBootstrapBrand(BootstrapBrand bootstrapBrand) {
        this.bootstrapBrand = bootstrapBrand;
        updateDropDownState();
    }

    @Override
    public BootstrapBrand getBootstrapBrand() {
        return bootstrapBrand;
    }

    @Override
    public boolean isTouchOutside() {
        if (dropdownWindow != null) {
            dropdownWindow.hide();
        }
        return false;
    }

    /**
     * A listener which provides methods relating to {@link BootstrapDropDown}
     */
    public interface OnDropDownItemClickListener {

        /**
         * Called when an item is clicked in a {@link BootstrapDropDown}
         *
         * @param parent the parent viewgroup
         * @param v the view
         * @param id the id
         */
        void onItemClick(ComponentContainer parent, Component v, int id);
    }

    static class DividerView extends Text {

        private final Paint paint;

        public DividerView(Context context) {
            super(context);
            paint = new Paint();
        }

    }
}
